package att_java;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;

public class GenTAM {
	
	public static final int TAILLE_INT = 1 ;
	public static final int TAILLE_BOOLEAN = 1 ;
	
	public static TDC tdc = new TDC() ;
	
	public static int getTaille (String nom_type) {
		int taille = 0 ;
		if (nom_type.equals("int")){
			taille = GenTAM.TAILLE_INT ;
		} else if (nom_type.equals("boolean")) {
			taille = GenTAM.TAILLE_BOOLEAN ;
		} else if (nom_type.equals("void")) {
			taille = 0 ;
		} else if (nom_type.equals("Constructeur!")) {
			taille = 1 ;
		} else {
			// On fait confiance a notre compilateur, on ne peut que demander
			// la taille d'un type qui est deja dans la TDC.
			taille = GenTAM.tdc.chercher(nom_type).getTaille() ;
		}
		return taille ;
	}
	
	public static String genTV () {
		String code = "; TABLES DES VIRTUELLES" + "\n" ;
		
		for (INFO_CLASSE classe : GenTAM.tdc.table) {
			code = code + "\n" + "; TABLE DES VIRTUELLES DE " + classe.getNom() + "\n" ;
			for (INFO_METHODE methode : classe.getTDM().table) {
				code = code + "LOADA " + methode.getEtiquette() + " ; Deplacement de " + methode.getDeplacement() + " \n" ;
			}
			code = code + "\n" ;
		}
		
		return code ;
	}
	
	public static TDC getTDC ( ) {
		return GenTAM.tdc ;
	}
	
	private String nom;
	
	public GenTAM(String nom_){
		nom = nom_;
	}
	
	// compteur pour le generateur d'etiquettes
	private static int cpt = 0;
	
	// genere une etiquette differente des autres
	public String genEtiq() {
		return "X" + cpt++;
	}
	
	
	// genere le code d'une fonction
	public String genMeth(INFO_METHODE methode, INFO_CLASSE classe, String corps){
		if(methode.getNom().equals("main")){
			return "_main\n" + corps + "HALT\n";
		}
		else{
			return "_" + classe.getNom() + "_" + methode.getNom() + "\n" + corps; 
		}
	}
	
	// genere le code du constructeur
	public String genConstructeur(INFO_METHODE methode, INFO_CLASSE classe, String corps){
		int taille_retour = 1;
		int taille_params = methode.getTDV().getTaille();
		return "_" + classe.getNom() + "_" + methode.getNom() + "\n"
		+ "LOADL " + classe.getTaille() + "\n"
		+ "SUBR Malloc\n"
		+ "STORE (1) -1[LB]\n"
		+ corps
		+ "LOAD (1) -1[LB]\n"
		+ "RETURN (" + taille_retour + ") " + taille_params + "\n";
	}
	
	// genere le code d'un bloc
	public String genBloc(String corps, int tailleADepiller){
		return corps + "POP (0) " + tailleADepiller + "\n";
	}
	
	// genere le code d'une declaration
	public String genDecl(String type, String code){
		if(code.equals("")){
			return "PUSH " + getTaille(type) + "\n";
		}
		else{
			return code;
		}
	}
	
	// genere le code de la conditionnelle
	public String genIfThenElse(String cond, String blocVrai, String blocFaux){
		String etiq = genEtiq();
		String label_else = "else__" + etiq; 
		String label_fin = "fin_if__" + etiq;
		return cond + " JUMPIF (0) " + label_else + "\n"
		+ blocVrai + "\n"
		+ "JUMP " + label_fin + "\n"
		+ label_else + "\n"
		+ blocFaux + "\n"
		+ label_fin + "\n";
	}
	
	// genere le code de retour
	public String genRetour(String code, INFO_METHODE methode){
		int taille_retour = getTaille(methode.getNomType());
		int taille_params = methode.getTDV().getTaille();
		return code
		+ "RETURN (" + taille_retour + ") " + taille_params + "\n";
	}
	
	// genere le code du tant que
	public String genWhile(String cond, String bloc){
		String etiq = genEtiq();
		String label_debut = "while_debut__" + etiq;
		String label_fin = "while_fin__" + etiq;
		return label_debut + " " + cond + " JUMPIF (0) " + label_fin + "\n"
		+ bloc + "\n"
		+ "JUMP " + label_debut + "\n"
		+ label_fin + "\n";
	}
	
	// genere le code pour l'arret de la machine
	public String genFin() {
		return "\tHALT\n";
	}
	
	
	/*********************** Type de Base ************************************/
	
	public String  genEntier(String ident){
		return "\tLOADL "+ident+"\n";
	}
	public String genBool(String b){
		if(b.equals("true")){
			return "\tLOADL 1 \n";
		}else{
			return "\tLOADL 0 \n";
		}
	}
	
	/************************** Entier ***************************************/
	
	public String genIntAdd(){
		return "\tSUBR  IAdd \n";
	}
	public String genIntSub(){
		return "\tSUBR  ISub \n";
	}
	public String genIntNeg(){ // le moins unaire
		return "\tSUBR  INeg \n";
	}
	public String genIntMul(){
		return "\tSUBR  IMul \n";
	}
	public String genIntDiv(){
		return "\tSUBR  IDiv \n";
	}
	public String genIntMod(){
		return "\tSUBR  IMod \n";
	}
	public String genIntTestEq(){
		return "\tSUBR  IEq \n";
	}
	public String genIntTestNEq(){
		return "\tSUBR  INeq \n";
	}
	public String genIntTestLss(){
		return "\tSUBR  ILss \n";
	}
	public String genIntTestLEq(){
		return "\tSUBR  ILeq \n";
	}
	public String genIntTestGtr(){
		return "\tSUBR  IGtr \n";
	}
	public String genIntTestGEq(){
		return "\tSUBR  IGeq \n";
	}
	
	/*********************** Boolean *****************************************/
	
	public String genBoolNeg(){
		return "\tSUBR BNeg \n";
	}
	public String genBoolOr(){
		return "\tSUBR BOr \n";
	}        
	public String genBoolAnd(){
		return "\tSUBR BAnd \n";
	}
	
	/********************** Appel ********************************************/
	
	public String genAppelConstr(String constr, String param){
		return param + "\tLOADL 0\n\tLOADL 0\n\tLOADA _"+constr+"_"+constr+"\n\tCALLI\n";
	}
	
	public String genAppelMethode(INFO_METHODE meth, String param){ 
		int dep = meth.getDeplacement();
		int tailleParams = meth.getTDV().getTaille();
		return param
		+ "LOAD (1) -" + tailleParams  + "[ST]\n"
		+ "LOADL 0\n"
		+ "LOAD (1) -2[ST]\n"
		+ "LOADI (1)\n"
		+ "POP (" + (tailleParams+2) + ") 1\n"
		+ "LOADL "+dep+"\n"
		+ "SUBR IAdd\n"
		+ "LOADI (1)\n"
		+ "CALLI\n";
	}
	
	public String genAppelAttribut(INFO_ATTRIBUT attribut){
		int dep = attribut.getDeplacement();
		return "LOADL "+dep+"\nSUBR IAdd\n"/*+"LOADI (1)\n"*/;
	}
	
	public String genAppelVariable(INFO_VARIABLE variable){
		int dep = variable.getDeplacement();
		return "POP (0) 1\nLOADA "+ dep + "[LB]\n" /*+ "LOADI (1)\n"*/; // "LOAD (1) " + dep + "[LB]";
	}
	
	public String genAffect(INFO_VARIABLE var){
		int taille;
		String nom = var.getNomType();
		if(nom.equals("int") || nom.equals("bool")){
			taille = 1;
		}else{
			taille = 1;
		}
		return "\tSTORE " +"("+taille+")"+var.getDeplacement()+"[SB]";
	}
	
	public String genProg(String tv, String code){
		return tv + "LOADL 0\nLOADL 0\nLOADA _main\nCALLI\nHALT\n" + code;
	}
	
	/*public String coupeDerniereLigne(String code){
		return  code.substring(0, code.length() - 11);
	
	}*/
	
	public String genAffectation(String gauche, String droite,boolean resgauche,  boolean res){
		if(droite.equals("")){
			String s = "";
			//if(resgauche){
			//	s = "LOADI (1)\n";
			//}
			return gauche + s;
		}
		else{
			String s = "";
			if(res){
				s = "LOADI (1)\n";
			}
			return droite + s + gauche + "STOREI (1)\n";
		}
	}
	
	
	public void writeFile(String code) {
		try {
			PrintWriter pw = new PrintWriter(new FileOutputStream(nom + ".tam"));
			pw.println(";;; code TAM engendre pour " + nom);
			pw.print(code);
			pw.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
	}
	
	
}
